﻿var oneSecond = 1000;
var oneMinute = oneSecond * 60;
var elements = {};

String.prototype.trim = function () {
    return this.replace(/^\s+|\s+$/g, "");
}

function updateDisplayGPS(data) {
    if (pref().method != "gps") { return; }
    updateDisplay(data);
}

function updateDisplayCode(data) {
    if (pref().method != "code") { return; }
    updateDisplay(data);
}

var _cachedData = null;
var _lastData = null;
var _dataTimeStamp = new Date(0);
function updateDisplay(data) {
    _cachedData = data;
    _dataTimeStamp = new Date();
    clearDisplay();

    for (var i = 0; i < data.length; i++) {
        var stopInfo = data[i].Stop;
        var stop = generateStop(stopInfo, data[i].Routes, data[i].Trips);

        if (isFavourite(getStopCode(stopInfo))) {
            elements.placeholderFavourites.append(stop);
        }
        else {
            elements.placeholderRegulars.append(stop);
        }
    }
    if (!objectsEqual(_lastData, data))
    { $(document).scrollTo(elements.placeholderFavourites); }
    _lastData = data;
}
function clearDisplay() {
    elements.placeholderFavourites.empty();
    elements.placeholderRegulars.empty();
}

function objectsEqual(A, B) {
    if (!A) { return !B; }
    if (A === B) { return true; }
    return JSON.stringify(A) == JSON.stringify(B);
}

var lastRefresh =
{
    "serverTimeStamp": new Date(0),
    "minute": -1
};
function refreshFromCache(forceRefresh) {
    if (!_cachedData)
    { return; }

    var minute = (new Date()).getMinutes();
    if (lastRefresh.serverTimeStamp == _dataTimeStamp) {
        if (!forceRefresh && minute == lastRefresh.minute) {
            //Nothing has changed, don't bother updating
            return;
        }
    }
    lastRefresh.serverTimeStamp = _dataTimeStamp;
    lastRefresh.minute = minute;

    var dataAge = currentTime() - _dataTimeStamp;
    if (dataAge > DataExpiration) {
        clearDisplay();
        refreshFromServer();
    }
    else {
        updateDisplay(_cachedData);
    }
}

function currentTime()
{ return (new Date()).getTime(); }

function refreshFromServer() {
    if (!_cachedData)
    { return; }

    var requestAge = currentTime() - lastServerRequest;
    if (requestAge < serverRequestTimeout)
    { return; }
    lastServerRequest = new Date();
    if (pref().method == "gps") {
        updateGPSLocation();
    } else {
        searchStops(pref().stop, updateDisplayCode, requestFailed);
    }
}

var refreshDisplayPeriod = oneMinute * 0.25;
var refreshDataPeriod = oneMinute * 15;
var serverRequestTimeout = oneMinute * 1;
var DataExpiration = refreshDataPeriod * 2;
var lastServerRequest = new Date(0);

setInterval(refreshFromCache, refreshDisplayPeriod);
setInterval(refreshFromServer, refreshDataPeriod);

function requestFailed() {
    var reason = [];
    for (var i = 0; i < arguments.length; i++) {
        reason.push(JSON.stringify(arguments[i]));
    }
    alert("request failed: " + reason.join(", "));
}

var _pref = null;
function pref() {
    if (_pref == null) {
        var storedPref = $.cookie("pref");
        if (storedPref) {
            _pref = JSON.parse(storedPref);
        }
    }
    if (_pref == null) {
        _pref = {
            "method": "code",
            "stop": "2577",
            "favourites": []
        };
    }
    return _pref;
}
function savePref() {
    $.cookie("pref", JSON.stringify(_pref), { expires: 365 });
}

function isFavourite(favouriteCode) {
    return ($.inArray(favouriteCode, pref().favourites) >= 0);
}

function addFavourite(favouriteCode) {
    if (pref().favourites.indexOf(favouriteCode) != -1) {
        // don't add if it already exist
        return;
    }
    pref().favourites.push(favouriteCode);
}

function removeFavourite(favouriteCode) {
    var index = pref().favourites.indexOf(favouriteCode);
    pref().favourites.splice(index, 1);
}

function getStopCode(stopInfo) {
    return stopInfo.Code + ' ' + stopInfo.Description;
}

function navMethod(method) {
    if (!method) { return; }
    elements.methodSelector.find("input[type='radio'][value='" + method + "']").attr("checked", "checked");

    // If the method has changed
    if (pref().method != method) {
        // Clear cache and display 
        _cachedData = null;
        clearDisplay();
        //Save in the cookie
        pref().method = method;
        savePref();
    }

    if (method == "gps") {
        elements.statusBar.addClass("GPS");
        elements.placeholderFavourites.html("Fetching your location with HTML 5 geolocation...");
        updateGPSLocation();
    } else {
        elements.statusBar.removeClass("GPS");
        StopCodeChanged();
    }
}

function StopCodeChanged() {
    var stopCode = elements.manualCodeText.val();
    pref().stop = stopCode;
    savePref();
    if (pref().method != "code") { return; }
    searchStops(stopCode, updateDisplayCode, requestFailed);
}

function GPSLocationChanged(position) {
    if (pref().method != "gps") { return; }
    elements.placeholderFavourites.html("Fetching Stops with your location...");
    elements.gpsStatusLattitude.text(position.coords.latitude);
    elements.gpsStatusLongitude.text(position.coords.longitude);
    elements.gpsStatusAccuracy.text(position.coords.accuracy);
    // The address is not always available (null)
    if (position.address) {
        var address = position.address.streetNumber + " " +
                      position.address.street + ", " +
                      position.address.city + ", " +
                      position.address.region + " " +
                      position.address.postalCode + ", " +
                      position.address.country;
        elements.gpsStatusAddress.text(address);
    }
    searchNearby(position.coords.latitude, position.coords.longitude, updateDisplayGPS, requestFailed);
}

function GPSLocationError(error) {
    alert("Error: " + error);
    disableGPS();
}

function updateGPSLocation() {
    // By using the 'maximumAge' option, the position object is guaranteed to be at most 10 minutes old.
    navigator.geolocation.getCurrentPosition(GPSLocationChanged, GPSLocationError, { maximumAge: 600000 });
}

function disableGPS() {
    navMethod("code");
    elements.methodSelectorRB.attr("disabled", "disabled");
}

function init() {
    try {
        elements.placeholderFavourites = $("#StopsFavourites");
        elements.placeholderRegulars = $("#StopsRegulars");
        elements.methodSelector = $("#MethodSelector");
        elements.methodSelectorRB = elements.methodSelector.find("input[type='radio']");

        elements.statusBar = $("#StatusBar");

        elements.gpsStatus = $("#GPS_Status");
        elements.gpsStatusLattitude = elements.gpsStatus.find("#Lattitude");
        elements.gpsStatusLongitude = elements.gpsStatus.find("#Longitude");
        elements.gpsStatusAccuracy = elements.gpsStatus.find("#Accuracy");
        elements.gpsStatusAddress = elements.gpsStatus.find("#Address");

        elements.manualCode = $("#ManualCode");
        elements.manualCodeText = elements.manualCode.find("input[type='text']");
        elements.manualCodeButton = elements.manualCode.find("input[type='button']");

        elements.manualCodeText.change(StopCodeChanged);
        elements.manualCodeButton.click(StopCodeChanged);
        elements.methodSelectorRB.change(function () {
            newNavMethod = elements.methodSelector.find("input[type='radio']:checked").val();
            navMethod(newNavMethod);
        });

        elements.manualCodeText.val(pref().stop);

        // Some navigator doesn't support the geolocation
        if (!navigator.geolocation) {
            disableGPS();
        } else {
            navMethod(pref().method);
        }

        window.onbeforeunload = function () { tearDown(); };
        $(window).unload(tearDown);
    }
    catch (err)
    { alert(err); }
}

var _didTearDown = false;
function tearDown() {
    if (_didTearDown) { return; }
    _didTearDown = true;
    savePref();
}

$(init);